<template>
  <div class="rag-container">
    <el-card class="rag-card">
      <div slot="header" class="card-header">
        <h2><i class="el-icon-office-building"></i> 智能知识库检索</h2>
      </div>

      <div class="rag-content">
        <!-- 左侧配置面板 -->
        <div class="config-panel">
          <!-- 面板标题 -->
          <div class="config-header">
            <div class="config-icon">
              <i class="el-icon-folder-opened"></i>
            </div>
            <h3 class="config-title">知识库配置</h3>
            <div class="config-subtitle">选择要检索的知识库</div>
          </div>

          <!-- 模型调用选择 -->
          <div class="model-select-section" style="padding: 0 20px 10px 20px">
            <el-form label-position="top">
              <el-form-item label="模型调用方式">
                <el-select v-model="modelType" placeholder="请选择模型">
                  <el-option label="Ollama" value="ollama" />
                  <el-option label="硅基流动AI" value="ai" />
                </el-select>
              </el-form-item>
            </el-form>
          </div>

          <!-- 知识库选择区域 -->
          <div class="kb-select-section">
            <div class="kb-select-header">
              <i class="el-icon-collection-tag"></i>
              <span>知识库检索</span>
            </div>
            <el-form label-position="top" class="kb-form">
              <el-form-item>
                <el-select
                  v-model="selectedRagTag"
                  placeholder="请选择知识库"
                  class="kb-select"
                  popper-class="kb-select-dropdown"
                >
                  <el-option
                    v-for="tag in ragTags"
                    :key="tag"
                    :label="tag"
                    :value="tag"
                    class="kb-option"
                  >
                    <div class="kb-option-content">
                      <i class="el-icon-document"></i>
                      <span>{{ tag }}</span>
                    </div>
                  </el-option>
                </el-select>
              </el-form-item>
            </el-form>
          </div>

          <!-- 当前选择状态 -->
          <div class="current-kb-status" v-if="selectedRagTag">
            <div class="status-icon">
              <i class="el-icon-success"></i>
            </div>
            <div class="status-content">
              <div class="status-title">当前知识库</div>
              <div class="status-value">{{ selectedRagTag }}</div>
            </div>
          </div>

          <!-- 知识库管理按钮 -->
          <div class="kb-actions">
            <el-button
              class="kb-manage-btn"
              type="primary"
              @click="gotoKbManage"
              icon="el-icon-setting"
            >
              <span class="btn-text">知识库管理</span>
              <i class="el-icon-arrow-right btn-arrow"></i>
            </el-button>
          </div>

          <!-- 装饰元素 -->
          <div class="config-decoration">
            <div class="decoration-dot dot-1"></div>
            <div class="decoration-dot dot-2"></div>
            <div class="decoration-dot dot-3"></div>
          </div>
        </div>

        <!-- 右侧对话区域 -->
        <div class="chat-panel">
          <!-- 聊天区顶部操作栏 -->
          <div class="chat-toolbar">
            <el-button type="text" @click="clearChat" class="clear-all-btn">
              <i class="el-icon-delete"></i> 清空对话
            </el-button>
            <el-button type="text" @click="exportChat" class="export-btn">
              <i class="el-icon-download"></i> 导出对话
            </el-button>
          </div>

          <!-- 调试面板 -->
          <div class="debug-panel" v-if="showDebug">
            <el-alert
              :title="debugInfo.title"
              :description="debugInfo.description"
              :type="debugInfo.type"
              show-icon
              :closable="false"
            />
          </div>

          <!-- 知识库简介/欢迎语 -->
          <div class="chat-welcome">
            <el-alert
              :title="`当前知识库：${selectedRagTag || '未选择'}`"
              description="欢迎使用智能知识库检索，您可以在下方输入问题，AI助手将为您检索并解答。"
              type="info"
              show-icon
              :closable="false"
              class="welcome-alert"
            />
          </div>

          <div class="chat-messages" ref="chatMessages">
            <div
              v-for="(msg, index) in messages"
              :key="index"
              :class="[
                'message',
                msg.type === 'user' ? 'user-message' : 'ai-message',
              ]"
            >
              <!-- 头像 -->
              <div class="message-avatar">
                <img v-if="msg.type === 'user'" src="../img/profile.jpg" />
                <img v-else src="../img/ai-avatar.png" alt="AI助手" />
              </div>
              <div class="message-content">
                <div class="message-header">
                  <span class="message-role">
                    <i
                      :class="
                        msg.type === 'user'
                          ? 'el-icon-user'
                          : 'el-icon-s-custom'
                      "
                    ></i>
                    {{ msg.type === "user" ? "我" : "AI助手" }}
                  </span>
                  <el-button
                    type="text"
                    class="delete-message-btn"
                    @click="deleteMessage(index)"
                    v-if="!loading || index !== messages.length - 1"
                  >
                    <i class="el-icon-delete"></i>
                  </el-button>
                </div>
                <!-- 用户消息原样输出，AI消息用MarkdownPreview渲染 -->
                <div class="message-text" v-if="msg.type === 'user'">
                  {{ msg.content }}
                </div>
                <div v-else class="message-text">
                  <span
                    v-if="
                      loading && index === messages.length - 1 && !msg.content
                    "
                  >
                    <span class="loading-dots">正在思考中</span>
                  </span>
                  <MarkdownPreview
                    v-else
                    :content="msg.content"
                    class="markdown-preview"
                  />
                </div>
              </div>
            </div>

            <!-- 加载指示器 -->
            <div
              v-if="
                loading &&
                (!messages.length ||
                  messages[messages.length - 1].type !== 'ai')
              "
              class="loading-message"
            >
              <div class="message ai-message">
                <div class="message-avatar">
                  <img src="../img/ai-avatar.png" alt="AI助手" />
                </div>
                <div class="message-content">
                  <div class="message-header">
                    <span class="message-role">
                      <i class="el-icon-s-custom"></i>
                      AI助手
                    </span>
                  </div>
                  <div class="message-text">
                    <span class="loading-dots">正在思考中</span>
                  </div>
                </div>
              </div>
            </div>

            <!-- 滚动到底部悬浮按钮 -->
            <el-button
              v-show="showScrollBtn"
              class="scroll-bottom-btn"
              icon="el-icon-bottom"
              circle
              @click="scrollToBottom"
            />
          </div>

          <!-- 固定在底部的输入区域 -->
          <div class="input-area-fixed">
            <!-- 快捷问题按钮 -->
            <div class="quick-questions">
              <el-tag
                v-for="(q, idx) in quickQuestions"
                :key="idx"
                @click="userInput = q"
                class="quick-question-tag"
                effect="plain"
              >
                {{ q }}
              </el-tag>
            </div>
            <div class="input-area">
              <el-input
                v-model="userInput"
                type="textarea"
                :rows="3"
                placeholder="请输入您的问题..."
                @keyup.enter.ctrl.native="sendMessage"
              />
              <div class="button-group">
                <el-button
                  type="primary"
                  @click="sendMessage"
                  :loading="loading"
                >
                  发送
                  <i class="el-icon-position el-icon--right"></i>
                </el-button>
                <el-button
                  v-if="loading"
                  type="danger"
                  class="pause-btn"
                  @click="pauseAI"
                >
                  暂停回复
                </el-button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </el-card>
  </div>
</template>

<script>
import {
  generateStreamRagAnswer,
  generateStreamRagAnswerDirect,
} from "@/api/ollama";
import { getRagTagList } from "@/api/rag";
import MarkdownPreview from "@/components/MarkdownPreview/index.vue";
import { getStreamRagCompletion } from "@/api/ai"; // 新增ai.js流式接口

export default {
  name: "RagChat",
  components: { MarkdownPreview },
  data() {
    return {
      selectedRagTag: "",
      userInput: "",
      messages: [],
      loading: false,
      ragTags: [],
      showDebug: false,
      debugInfo: {
        title: "",
        description: "",
        type: "",
      },
      quickQuestions: [
        "请简要介绍RAG检索技术",
        "如何上传文档到知识库？",
        "支持哪些文档格式？",
        "回答的准确性如何保证？",
        "如何管理知识库内容？",
      ],
      showScrollBtn: false,
      scrollTimeout: null,
      updateTimeout: null,
      modelType: "ollama", // 新增，默认ollama
      abortController: null, // 新增：用于中断AI流
    };
  },
  created() {
    this.getRagTags();
  },
  methods: {
    async getRagTags() {
      try {
        const res = await getRagTagList();
        if (res.code === "0000") {
          this.ragTags = res.data;
          if (this.ragTags.length > 0) {
            this.selectedRagTag = this.ragTags[0];
          }
        }
      } catch (error) {
        this.$message.error("获取知识库列表失败：" + error.message);
      }
    },
    async sendMessage() {
      if (!this.userInput.trim()) {
        this.$message.warning("请输入问题");
        return;
      }

      if (!this.selectedRagTag) {
        this.$message.warning("请选择知识库");
        return;
      }

      // 添加用户消息
      this.messages.push({
        type: "user",
        content: this.userInput,
      });

      // 只push一次AI消息
      const aiMessageIndex = this.messages.length;
      this.messages.push({
        type: "ai",
        content: "",
      });

      this.loading = true;
      // 新增：创建AbortController
      const controller = new AbortController();
      this.abortController = controller;
      // 优化提示词，要求更精准简洁的回答
      const enhancedQuestion = `请基于知识库内容，简洁准确地回答以下问题。回答要精准、实用，避免冗余信息：${this.userInput}`;
      this.userInput = "";

      try {
        if (this.modelType === "ollama") {
          // 使用ollama流式接口
          const response = await generateStreamRagAnswerDirect(
            "deepseek-r1:1.5b",
            this.selectedRagTag,
            enhancedQuestion,
            { signal: controller.signal }
          );

          if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
          }

          const reader = response.body.getReader();
          const decoder = new TextDecoder("utf-8", { fatal: false });
          let buffer = "";
          let isFirstChunk = true;
          let typingQueue = [];
          let typing = false;

          // 打字机效果函数
          const typeWriter = (aiMessageIndex) => {
            if (typingQueue.length === 0) {
              typing = false;
              return;
            }
            typing = true;
            const char = typingQueue.shift();
            this.messages[aiMessageIndex].content += char;
            if (this.updateTimeout) {
              clearTimeout(this.updateTimeout);
            }
            this.updateTimeout = setTimeout(() => {
              this.scrollToBottom();
            }, 100);
            setTimeout(() => typeWriter(aiMessageIndex), 18); // 打字速度
          };

          while (true) {
            const { done, value } = await reader.read();

            if (done) {
              break;
            }

            // 解码当前块并添加到缓冲区
            const chunk = decoder.decode(value, { stream: true });
            buffer += chunk;

            // 按行分割数据
            const lines = buffer.split("\n");

            // 保留最后一行（可能不完整）
            buffer = lines.pop() || "";

            // 处理完整的行
            for (const line of lines) {
              // 只处理一层 data: 前缀
              let data = line.trim();
              while (data.startsWith("data:")) {
                data = data.replace(/^data:/, "").trim();
              }
              if (!data) continue;
              if (data === "[DONE]") {
                this.$nextTick(() => {
                  this.scrollToBottom();
                });
                return;
              }
              try {
                const parsed = JSON.parse(data);
                // 只提取 content 字段内容
                let content = null;
                if (
                  parsed.result &&
                  parsed.result.output &&
                  parsed.result.output.content
                ) {
                  content = parsed.result.output.content;
                } else if (parsed.output && parsed.output.content) {
                  content = parsed.output.content;
                } else if (parsed.content) {
                  content = parsed.content;
                } else if (parsed.results && parsed.results.length > 0) {
                  const firstResult = parsed.results[0];
                  if (firstResult.output && firstResult.output.content) {
                    content = firstResult.output.content;
                  }
                }
                if (content && content !== "<think>" && content.trim() !== "") {
                  // 打字机效果：将content逐字加入队列
                  for (let char of content) {
                    typingQueue.push(char);
                  }
                  if (!typing) typeWriter(aiMessageIndex);
                }
              } catch (e) {
                // 忽略无法解析的内容
              }
            }
          }

          // 处理缓冲区中剩余的数据
          if (buffer.trim()) {
            const lines = buffer.split("\n");
            for (const line of lines) {
              let data = line.trim();
              while (data.startsWith("data:")) {
                data = data.replace(/^data:/, "").trim();
              }
              if (!data) continue;
              if (data === "[DONE]") {
                return;
              }
              try {
                const parsed = JSON.parse(data);
                let content = null;
                if (
                  parsed.result &&
                  parsed.result.output &&
                  parsed.result.output.content
                ) {
                  content = parsed.result.output.content;
                } else if (parsed.output && parsed.output.content) {
                  content = parsed.output.content;
                } else if (parsed.content) {
                  content = parsed.content;
                }
                if (content && content !== "<think>" && content.trim() !== "") {
                  for (let char of content) {
                    typingQueue.push(char);
                  }
                  if (!typing) typeWriter(aiMessageIndex);
                }
              } catch (e) {
                // 忽略解析错误
              }
            }
          }

          // 最终滚动到底部
          this.$nextTick(() => {
            this.scrollToBottom();
          });
        } else if (this.modelType === "ai") {
          // 使用ai.js流式接口
          const params = {
            model: "Qwen/Qwen2.5-7B-Instruct", // 可根据需要调整
            ragTag: this.selectedRagTag,
            message: enhancedQuestion,
          };
          const response = await getStreamRagCompletion(params, {
            signal: controller.signal,
          });
          if (!response) {
            throw new Error("AI接口无响应");
          }
          const reader = response.body.getReader();
          const decoder = new TextDecoder("utf-8", { fatal: false });
          let buffer = "";
          while (true) {
            const { done, value } = await reader.read();
            if (done) break;
            const chunk = decoder.decode(value, { stream: true });
            buffer += chunk;
            const lines = buffer.split("\n");
            buffer = lines.pop() || "";
            for (const line of lines) {
              let data = line.trim();
              while (data.startsWith("data:")) {
                data = data.replace(/^data:/, "").trim();
              }
              if (!data) continue;
              if (!data.startsWith("{") || !data.endsWith("}")) continue;
              if (data === "[DONE]") {
                this.$nextTick(() => {
                  this.scrollToBottom();
                });
                return;
              }
              try {
                const parsed = JSON.parse(data);
                let content = "";
                if (
                  parsed.result &&
                  parsed.result.output &&
                  typeof parsed.result.output.content === "string"
                ) {
                  content = parsed.result.output.content;
                } else if (
                  parsed.output &&
                  typeof parsed.output.content === "string"
                ) {
                  content = parsed.output.content;
                } else if (typeof parsed.content === "string") {
                  content = parsed.content;
                }
                if (typeof content !== "string") content = "";
                if (content && content !== "<think>" && content.trim() !== "") {
                  this.messages[aiMessageIndex].content += content;
                  if (this.updateTimeout) {
                    clearTimeout(this.updateTimeout);
                  }
                  this.updateTimeout = setTimeout(() => {
                    this.scrollToBottom();
                  }, 100);
                }
              } catch (e) {
                console.log("AI.js parse error:", e, data);
              }
            }
          }
          if (buffer.trim()) {
            const lines = buffer.split("\n");
            for (const line of lines) {
              let data = line.trim();
              while (data.startsWith("data:")) {
                data = data.replace(/^data:/, "").trim();
              }
              if (!data) continue;
              if (!data.startsWith("{") || !data.endsWith("}")) continue;
              if (data === "[DONE]") {
                return;
              }
              try {
                const parsed = JSON.parse(data);
                let content = "";
                if (
                  parsed.result &&
                  parsed.result.output &&
                  typeof parsed.result.output.content === "string"
                ) {
                  content = parsed.result.output.content;
                } else if (
                  parsed.output &&
                  typeof parsed.output.content === "string"
                ) {
                  content = parsed.output.content;
                } else if (typeof parsed.content === "string") {
                  content = parsed.content;
                }
                if (typeof content !== "string") content = "";
                if (content && content !== "<think>" && content.trim() !== "") {
                  this.messages[aiMessageIndex].content += content;
                }
              } catch (e) {
                console.log("AI.js parse error:", e, data);
              }
            }
          }
          this.$nextTick(() => {
            this.scrollToBottom();
          });
        }
      } catch (error) {
        if (error.name === "AbortError") {
          this.$message.info("AI回复已暂停");
        } else {
          this.$message.error("请求失败：" + error.message);
          this.messages[aiMessageIndex].content =
            "❌ 抱歉，发生了错误，请重试。\n\n💡 可能的原因：\n1. 网络连接问题\n2. 服务器暂时不可用\n3. 知识库配置问题";
        }
      } finally {
        this.loading = false;
        this.abortController = null;
      }
    },
    pauseAI() {
      if (this.abortController) {
        this.abortController.abort();
        this.abortController = null;
      }
      this.loading = false;
    },

    clearChat() {
      this.$confirm("确定要清空所有对话吗？", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
        this.messages = [];
      });
    },

    scrollToBottom() {
      if (this.scrollTimeout) {
        clearTimeout(this.scrollTimeout);
      }
      this.scrollTimeout = setTimeout(() => {
        if (this.$refs.chatMessages) {
          this.$refs.chatMessages.scrollTop =
            this.$refs.chatMessages.scrollHeight;
        }
        this.showScrollBtn = false;
      }, 50);
    },

    handleScrollBtnShow() {
      if (!this.$refs.chatMessages) return;
      const el = this.$refs.chatMessages;
      this.showScrollBtn =
        el.scrollHeight - el.scrollTop - el.clientHeight > 100;
    },

    deleteMessage(index) {
      this.messages.splice(index, 1);
    },

    exportChat() {
      if (!this.messages.length) {
        this.$message.warning("暂无对话可导出");
        return;
      }
      const mdLines = this.messages.map((msg) => {
        if (msg.type === "user") {
          return `**我：** ${msg.content}`;
        } else {
          return `**AI助手：**\n${msg.content}`;
        }
      });
      const txtLines = this.messages.map((msg) => {
        if (msg.type === "user") {
          return `我: ${msg.content}`;
        } else {
          return `AI助手: ${msg.content.replace(/\n/g, " ")}`;
        }
      });
      const mdContent = mdLines.join("\n\n");
      const txtContent = txtLines.join("\n");
      this.$confirm("请选择导出格式", "导出对话", {
        confirmButtonText: "导出为Markdown",
        cancelButtonText: "导出为TXT",
        distinguishCancelAndClose: true,
        type: "info",
      })
        .then(() => {
          this.downloadFile(mdContent, "chat.md");
        })
        .catch((action) => {
          if (action === "cancel") {
            this.downloadFile(txtContent, "chat.txt");
          }
        });
    },

    downloadFile(content, filename) {
      const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
      const link = document.createElement("a");
      link.href = URL.createObjectURL(blob);
      link.download = filename;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    },

    gotoKbManage() {
      this.$router.push({ path: "/knowledge/repository" });
    },
  },
  mounted() {
    this.scrollToBottom();
    this.$refs.chatMessages &&
      this.$refs.chatMessages.addEventListener(
        "scroll",
        this.handleScrollBtnShow
      );
  },
  beforeDestroy() {
    this.$refs.chatMessages &&
      this.$refs.chatMessages.removeEventListener(
        "scroll",
        this.handleScrollBtnShow
      );

    // 清理定时器
    if (this.scrollTimeout) {
      clearTimeout(this.scrollTimeout);
    }
    if (this.updateTimeout) {
      clearTimeout(this.updateTimeout);
    }
  },
};
</script>

<style scoped>
.rag-container,
.rag-card,
.rag-content,
.chat-panel {
  height: 100vh !important;
  min-height: 0 !important;
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
}

.rag-content {
  flex-direction: row;
  flex: 1 1 0;
  min-height: 0;
  gap: 32px;
}

.config-panel {
  width: 280px;
  min-width: 240px;
  max-width: 320px;
  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
  border-radius: 20px;
  margin: 24px 0 24px 24px;
  padding: 0;
  box-shadow: 0 8px 32px rgba(102, 126, 234, 0.12);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  height: calc(100vh - 120px);
  position: relative;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.2);
}

.chat-panel {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  height: 100%;
  position: relative;
  background: #fff;
  margin: 24px 24px 24px 0;
  border-radius: 18px;
  box-shadow: 0 2px 8px rgba(102, 126, 234, 0.08);
}

.chat-toolbar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  padding: 12px 18px 0 18px;
  min-height: 40px;
}

.clear-all-btn {
  color: #e74c3c;
  font-weight: 700;
  font-size: 15px;
  border-radius: 8px;
  transition: background 0.2s;
}
.clear-all-btn:hover {
  background: #fbeaea;
}

.chat-messages {
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;
  padding: 16px 0 100px 0;
  background: #f8f9fa;
  border-radius: 0 0 18px 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.message {
  margin-bottom: 16px;
  display: flex;
  animation: fadeInUp 0.4s ease-out;
  align-items: flex-start;
  padding: 0 20px;
}
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.user-message {
  justify-content: flex-end;
  flex-direction: row-reverse;
}

.ai-message {
  justify-content: flex-start;
}

.message-avatar {
  width: 36px;
  height: 36px;
  margin: 0 10px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.message-avatar img {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid #e3e6eb;
  background: #fff;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}

.message-content,
.loading-message .message-content {
  min-width: 0;
  max-width: 78%;
  width: auto;
  box-sizing: border-box;
  padding: 10px 14px;
  border-radius: 16px;
  position: relative;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
  transition: all 0.3s ease;
  word-wrap: break-word;
  overflow-wrap: break-word;
  font-size: 14px;
  line-height: 1.5;
  background: #f8f9fa;
  color: #2c3e50;
  letter-spacing: 0.2px;
}

.user-message .message-content {
  background: linear-gradient(135deg, #e3edfa 0%, #b3d8fb 100%);
  color: #1a4a7a;
  border-radius: 16px 6px 16px 16px;
  border: 1px solid #b3d8fb;
  box-shadow: 0 2px 8px rgba(102, 126, 234, 0.08);
  margin-left: auto;
  font-weight: 500;
}

.ai-message .message-content {
  background: rgba(255, 255, 255, 0.98);
  border: 1px solid #e3e6eb;
  color: #2c3e50;
  border-radius: 6px 16px 16px 16px;
  box-shadow: 0 2px 8px rgba(102, 126, 234, 0.06);
  margin-right: auto;
  font-weight: 400;
}

.message-header {
  margin-bottom: 6px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.message-role {
  font-weight: 600;
  color: inherit;
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  opacity: 0.8;
  letter-spacing: 0.3px;
}

.delete-message-btn {
  opacity: 0;
  transition: all 0.3s ease;
  padding: 2px 6px;
  border-radius: 6px;
  color: #b0b0b0;
  font-size: 12px;
}
.message-content:hover .delete-message-btn {
  opacity: 1;
}
.delete-message-btn:hover {
  background-color: #fbeaea;
  color: #e74c3c;
}

.message-text {
  white-space: pre-wrap;
  color: inherit;
  font-size: 14px;
  word-wrap: break-word;
  overflow-wrap: break-word;
  max-width: 100%;
  line-height: 1.5;
  letter-spacing: 0.1px;
  text-align: justify;
}

.input-area-fixed {
  flex-shrink: 0;
  background: #fff;
  border-top: 1.5px solid #e3e6eb;
  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.04);
  border-radius: 0 0 18px 0;
  padding: 0;
  position: sticky;
  bottom: 0;
  left: 0;
  width: 100%;
  z-index: 10;
}

.input-area {
  padding: 8px 14px;
  display: flex;
  gap: 8px;
  align-items: flex-end;
  background: #fff;
  border-radius: 0 0 18px 0;
}

.input-area .el-textarea__inner {
  border-radius: 12px;
  border: 1.5px solid #b3d8fb;
  background: rgba(245, 247, 250, 0.95);
  font-size: 14px;
  transition: border-color 0.2s, box-shadow 0.2s;
  min-height: 32px;
  max-height: 80px;
  line-height: 1.4;
  letter-spacing: 0.1px;
}

.button-group {
  margin-top: 0;
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

.button-group .el-button {
  border-radius: 10px;
  padding: 6px 16px;
  font-weight: 600;
  font-size: 14px;
  transition: transform 0.15s, box-shadow 0.15s;
  border: none;
  background: linear-gradient(135deg, #205081 0%, #4e8ad1 100%);
  color: #fff;
  box-shadow: 0 2px 8px rgba(102, 126, 234, 0.1);
  letter-spacing: 0.3px;
}
.button-group .el-button:hover {
  transform: scale(1.02);
  box-shadow: 0 3px 12px rgba(102, 126, 234, 0.15);
}

.w-100 {
  width: 100%;
}

/* 自定义滚动条样式 */
.chat-messages::-webkit-scrollbar {
  width: 8px;
}

.chat-messages::-webkit-scrollbar-thumb {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 4px;
}

.chat-messages::-webkit-scrollbar-track {
  background: rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

/* 加载动画 */
.loading-dots {
  display: inline-block;
}
.loading-dots::after {
  content: "";
  animation: dots 1.5s steps(5, end) infinite;
}
@keyframes dots {
  0%,
  20% {
    content: "";
  }
  40% {
    content: ".";
  }
  60% {
    content: "..";
  }
  80%,
  100% {
    content: "...";
  }
}

/* 响应式设计 */
@media (max-width: 768px) {
  .rag-container {
    padding: 8px;
  }
  .rag-content {
    flex-direction: column;
  }
  .config-panel {
    width: 100%;
    border-right: none;
    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 16px;
    margin: 8px 0;
    height: auto;
    min-height: auto;
  }
  .config-header {
    padding: 16px 14px 12px 14px;
  }
  .config-icon {
    width: 36px;
    height: 36px;
  }
  .config-icon i {
    font-size: 18px;
  }
  .config-title {
    font-size: 15px;
  }
  .config-subtitle {
    font-size: 11px;
  }
  .kb-select-section {
    margin: 10px;
    padding: 16px 14px 10px 14px;
  }
  .current-kb-status {
    margin: 0 10px 10px 10px;
    padding: 10px;
  }
  .kb-actions {
    padding: 0 10px 16px 10px;
  }
  .chat-panel {
    margin: 8px 0;
    border-radius: 12px;
  }
  .message {
    padding: 0 10px;
    margin-bottom: 12px;
  }
  .message-content {
    max-width: 88%;
    font-size: 13px;
    padding: 8px 10px;
    line-height: 1.4;
  }
  .message-avatar {
    width: 30px;
    height: 30px;
    margin: 0 6px;
  }
  .message-avatar img {
    width: 28px;
    height: 28px;
  }
  .message-role {
    font-size: 11px;
  }
  .message-text {
    font-size: 13px;
    line-height: 1.4;
  }
  .input-area {
    padding: 6px 10px;
  }
  .chat-messages {
    padding: 0 0 70px 0;
  }
  .quick-questions {
    padding: 10px 14px 6px 14px;
    gap: 6px;
  }
  .quick-question-tag {
    font-size: 12px;
    padding: 5px 10px;
    border-radius: 16px;
    font-weight: 500;
    letter-spacing: 0.1px;
    min-height: 28px;
    line-height: 1.1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  .card-header h2 {
    font-size: 16px;
  }
  .card-header h2 i {
    font-size: 18px;
  }
}

.chat-welcome {
  padding: 0 20px 20px 20px;
  background: transparent;
}

.welcome-alert {
  border-radius: 12px;
  border: 1px solid #e3e6eb;
  background: rgba(255, 255, 255, 0.95);
  box-shadow: 0 2px 8px rgba(102, 126, 234, 0.06);
}

.quick-questions {
  display: flex;
  gap: 8px;
  padding: 14px 18px 10px 18px;
  flex-wrap: wrap;
  margin-bottom: 0;
  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
  border-top: 1px solid #e3e6eb;
  border-bottom: 1px solid #e3e6eb;
}
.quick-question-tag {
  cursor: pointer;
  font-size: 13px;
  font-weight: 500;
  border-radius: 18px;
  padding: 6px 14px;
  background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
  color: #2c3e50;
  border: 1px solid #d1ecf1;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  white-space: nowrap;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
  position: relative;
  overflow: hidden;
  letter-spacing: 0.2px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1.2;
  min-height: 32px;
}

.quick-question-tag::before {
  content: "";
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(255, 255, 255, 0.4),
    transparent
  );
  transition: left 0.5s;
}
.quick-question-tag:hover {
  background: linear-gradient(135deg, #e3edfa 0%, #b3d8fb 100%);
  color: #1a4a7a;
  transform: translateY(-1px);
  box-shadow: 0 3px 10px rgba(102, 126, 234, 0.12);
  border-color: #4e8ad1;
  font-weight: 600;
}
.quick-question-tag:hover::before {
  left: 100%;
}
.quick-question-tag:active {
  transform: translateY(0);
  box-shadow: 0 2px 6px rgba(102, 126, 234, 0.1);
}

.kb-manage-btn {
  margin-top: 24px;
  width: 100%;
  font-weight: 700;
  letter-spacing: 1px;
  border-radius: 8px;
  background: linear-gradient(135deg, #205081 0%, #4e8ad1 100%);
  color: #fff;
  border: none;
  box-shadow: 0 2px 8px rgba(102, 126, 234, 0.1);
  transition: background 0.2s;
}
.kb-manage-btn:hover {
  background: linear-gradient(135deg, #4e8ad1 0%, #205081 100%);
  color: #fff;
}

.scroll-bottom-btn {
  position: fixed;
  right: 38px;
  bottom: 120px;
  z-index: 99;
  background: linear-gradient(135deg, #205081 0%, #4e8ad1 100%);
  color: #fff;
  box-shadow: 0 2px 8px rgba(102, 126, 234, 0.18);
  border-radius: 50%;
  width: 48px;
  height: 48px;
  font-size: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.2s;
}
.scroll-bottom-btn:hover {
  background: linear-gradient(135deg, #4e8ad1 0%, #205081 100%);
  color: #fff;
}

.card-header {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px 0;
  border-bottom: 1px solid #e3e6eb;
  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
  border-radius: 12px 12px 0 0;
}

.card-header h2 {
  margin: 0;
  font-size: 20px;
  font-weight: 600;
  color: #205081;
  display: flex;
  align-items: center;
  gap: 8px;
}

.card-header h2 i {
  font-size: 22px;
  color: #4e8ad1;
}

/* 配置面板标题 */
.config-header {
  background: linear-gradient(135deg, #205081 0%, #4e8ad1 100%);
  padding: 24px 20px 20px 20px;
  text-align: center;
  color: white;
  position: relative;
  overflow: hidden;
}

.config-header::before {
  content: "";
  position: absolute;
  top: -50%;
  left: -50%;
  width: 200%;
  height: 200%;
  background: radial-gradient(
    circle,
    rgba(255, 255, 255, 0.1) 0%,
    transparent 70%
  );
  animation: rotate 20s linear infinite;
}

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.config-icon {
  width: 48px;
  height: 48px;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 12px auto;
  position: relative;
  z-index: 1;
}

.config-icon i {
  font-size: 24px;
  color: white;
}

.config-title {
  margin: 0 0 4px 0;
  font-size: 18px;
  font-weight: 600;
  position: relative;
  z-index: 1;
}

.config-subtitle {
  font-size: 13px;
  opacity: 0.9;
  position: relative;
  z-index: 1;
}

/* 知识库选择区域 */
.kb-select-section {
  padding: 24px 20px 16px 20px;
  background: white;
  margin: 16px;
  border-radius: 16px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
  border: 1px solid rgba(102, 126, 234, 0.1);
}

.kb-select-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
  color: #205081;
  font-weight: 600;
  font-size: 14px;
}

.kb-select-header i {
  font-size: 16px;
  color: #4e8ad1;
}

.kb-form {
  margin: 0;
}

.kb-select {
  width: 100%;
}

.kb-select .el-input__inner {
  border-radius: 12px;
  border: 2px solid #e3e6eb;
  background: #f8f9fa;
  font-size: 14px;
  transition: all 0.3s ease;
  height: 44px;
  line-height: 44px;
}

.kb-select .el-input__inner:focus {
  border-color: #4e8ad1;
  box-shadow: 0 0 0 3px rgba(78, 138, 209, 0.1);
  background: white;
}

.kb-select .el-input__suffix {
  color: #4e8ad1;
}

/* 下拉选项样式 */
.kb-option-content {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 0;
}

.kb-option-content i {
  color: #4e8ad1;
  font-size: 16px;
}

/* 当前选择状态 */
.current-kb-status {
  margin: 0 16px 16px 16px;
  padding: 16px;
  background: linear-gradient(135deg, #e8f5e8 0%, #d4edda 100%);
  border-radius: 12px;
  border: 1px solid #c3e6cb;
  display: flex;
  align-items: center;
  gap: 12px;
  animation: slideInUp 0.5s ease;
}

@keyframes slideInUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.status-icon {
  width: 32px;
  height: 32px;
  background: #28a745;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.status-icon i {
  color: white;
  font-size: 16px;
}

.status-content {
  flex: 1;
  min-width: 0;
}

.status-title {
  font-size: 12px;
  color: #155724;
  font-weight: 500;
  margin-bottom: 2px;
}

.status-value {
  font-size: 14px;
  color: #155724;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 知识库管理按钮区域 */
.kb-actions {
  padding: 0 16px 24px 16px;
  margin-top: auto;
}

.btn-text {
  font-size: 14px;
}

.btn-arrow {
  font-size: 12px;
  transition: transform 0.3s ease;
}

.kb-manage-btn:hover .btn-arrow {
  transform: translateX(4px);
}

/* 装饰元素 */
.config-decoration {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  pointer-events: none;
  overflow: hidden;
}

.decoration-dot {
  position: absolute;
  border-radius: 50%;
  background: linear-gradient(
    135deg,
    rgba(78, 138, 209, 0.1),
    rgba(102, 126, 234, 0.1)
  );
  animation: float 6s ease-in-out infinite;
}

.dot-1 {
  width: 8px;
  height: 8px;
  top: 20%;
  right: 15%;
  animation-delay: 0s;
}

.dot-2 {
  width: 12px;
  height: 12px;
  top: 60%;
  right: 10%;
  animation-delay: 2s;
}

.dot-3 {
  width: 6px;
  height: 6px;
  top: 80%;
  right: 25%;
  animation-delay: 4s;
}

@keyframes float {
  0%,
  100% {
    transform: translateY(0px) scale(1);
    opacity: 0.6;
  }
  50% {
    transform: translateY(-10px) scale(1.1);
    opacity: 1;
  }
}

/* 全局下拉菜单样式 */
:deep(.kb-select-dropdown) {
  border-radius: 12px;
  box-shadow: 0 8px 32px rgba(102, 126, 234, 0.15);
  border: 1px solid rgba(102, 126, 234, 0.1);
  overflow: hidden;
}

:deep(.kb-select-dropdown .el-select-dropdown__item) {
  padding: 12px 16px;
  transition: all 0.3s ease;
}

:deep(.kb-select-dropdown .el-select-dropdown__item:hover) {
  background: linear-gradient(135deg, #e3edfa 0%, #b3d8fb 100%);
  color: #205081;
}

:deep(.kb-select-dropdown .el-select-dropdown__item.selected) {
  background: linear-gradient(135deg, #205081 0%, #4e8ad1 100%);
  color: white;
}

:deep(.kb-select-dropdown .el-select-dropdown__item.selected:hover) {
  background: linear-gradient(135deg, #4e8ad1 0%, #205081 100%);
  color: white;
}

/* MarkdownPreview 组件样式优化 */
:deep(.message-text .markdown-preview) {
  font-size: 14px;
  line-height: 1.5;
  letter-spacing: 0.1px;
  color: inherit;
}

:deep(.message-text .markdown-preview h1),
:deep(.message-text .markdown-preview h2),
:deep(.message-text .markdown-preview h3),
:deep(.message-text .markdown-preview h4),
:deep(.message-text .markdown-preview h5),
:deep(.message-text .markdown-preview h6) {
  margin: 12px 0 8px 0;
  font-weight: 600;
  color: inherit;
  line-height: 1.4;
}

:deep(.message-text .markdown-preview h1) {
  font-size: 16px;
}
:deep(.message-text .markdown-preview h2) {
  font-size: 15px;
}
:deep(.message-text .markdown-preview h3) {
  font-size: 14px;
}
:deep(.message-text .markdown-preview h4) {
  font-size: 14px;
}
:deep(.message-text .markdown-preview h5) {
  font-size: 13px;
}
:deep(.message-text .markdown-preview h6) {
  font-size: 13px;
}

:deep(.message-text .markdown-preview p) {
  margin: 6px 0;
  line-height: 1.5;
}

:deep(.message-text .markdown-preview ul),
:deep(.message-text .markdown-preview ol) {
  margin: 6px 0;
  padding-left: 20px;
}

:deep(.message-text .markdown-preview li) {
  margin: 3px 0;
  line-height: 1.5;
}

:deep(.message-text .markdown-preview code) {
  background: rgba(0, 0, 0, 0.05);
  padding: 2px 4px;
  border-radius: 3px;
  font-size: 13px;
  font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
}

:deep(.message-text .markdown-preview pre) {
  background: rgba(0, 0, 0, 0.03);
  padding: 8px 12px;
  border-radius: 6px;
  margin: 8px 0;
  overflow-x: auto;
  border: 1px solid rgba(0, 0, 0, 0.1);
}

:deep(.message-text .markdown-preview pre code) {
  background: none;
  padding: 0;
  border-radius: 0;
}

:deep(.message-text .markdown-preview blockquote) {
  border-left: 3px solid #4e8ad1;
  margin: 8px 0;
  padding: 4px 0 4px 12px;
  background: rgba(78, 138, 209, 0.05);
  border-radius: 0 4px 4px 0;
}

:deep(.message-text .markdown-preview table) {
  border-collapse: collapse;
  width: 100%;
  margin: 8px 0;
  font-size: 13px;
}

:deep(.message-text .markdown-preview th),
:deep(.message-text .markdown-preview td) {
  border: 1px solid rgba(0, 0, 0, 0.1);
  padding: 6px 8px;
  text-align: left;
}

:deep(.message-text .markdown-preview th) {
  background: rgba(0, 0, 0, 0.05);
  font-weight: 600;
}

:deep(.message-text .markdown-preview a) {
  color: #4e8ad1;
  text-decoration: none;
}

:deep(.message-text .markdown-preview a:hover) {
  text-decoration: underline;
}

:deep(.message-text .markdown-preview strong) {
  font-weight: 600;
  color: inherit;
}

:deep(.message-text .markdown-preview em) {
  font-style: italic;
  color: inherit;
}
</style>